#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>
#include "Cond.hpp"
#include "Mutex.hpp"

namespace BlockQueueModule
{
    using namespace LockModule;
    using namespace CondMudule;
    static const int gcap = 10;
    template <typename T>
    class BlockQueue
    {
    private:
        bool IsFull()
        {
            return _cap == _q.size();
        }

        bool IsEmpty()
        {
            return _q.empty();
        }

    public:
        BlockQueue(int cap = gcap) : _cap(cap), cwait_num(0), pwait_num(0)
        {
            pthread_mutex_init(&_mutex, nullptr);
            pthread_cond_init(&_pcond, nullptr);
            pthread_cond_init(&_ccond, nullptr);
        }

        void Equeue(const T &in)
        {
            pthread_mutex_lock(&_mutex);
            while (IsFull())
            {
                pwait_num++;
                pthread_cond_wait(&_pcond, &_mutex); // 等待唤醒并重新申请锁
                pwait_num--;
            }

            _q.push(in);

            if (cwait_num)
            {
                pthread_cond_signal(&_ccond);
            }
            pthread_mutex_unlock(&_mutex);
        }

        void Pop(T *out)
        {
            pthread_mutex_lock(&_mutex);
            while (IsEmpty())
            {
                cwait_num++;
                pthread_cond_wait(&_ccond, &_mutex); // 等待唤醒并重新申请锁
                cwait_num--;
            }

            *out = _q.front();
            _q.pop();

            if (pwait_num)
            {
                pthread_cond_signal(&_pcond);
            }
            pthread_mutex_unlock(&_mutex);
        }
        ~BlockQueue()
        {
            pthread_mutex_destroy(&_mutex);
            pthread_cond_destroy(&_pcond);
            pthread_cond_destroy(&_ccond);
        }

    private:
        std::queue<T> _q;
        int _cap;               // 最大容量
        pthread_mutex_t _mutex; // 互斥，锁
        pthread_cond_t _pcond;  // 生产者的条件变量
        pthread_cond_t _ccond;  // 消费者的条件变量

        int cwait_num;
        int pwait_num;
    };
}